package apibasej.db.sql.wrapper;

import java.util.ArrayList;
import java.util.Collection;

import apibasej.db.metadata.Col;



// T é o tipo do value
public class Filter<T> extends SqlPart<T> implements InterfaceColValue<T>{

	// TODO criar classe para todas consts !!!
	public static final String DEFAULT_OPERATOR_FOR_VALUE = "=";
	public static final String DEFAULT_STR_FILTER_AFTER_OPERATOR = "?";
	//public static final String DEFAULT_OPERATOR_BETWEEN_FILTERS = "AND";
	
	// dependendo das strings setadas 
	public Filter(WrapperTab wrapperTab, Col<T> col, T value) {
		super(wrapperTab, col);
		this.value = value;
	}
	
	private T value; // valor para setar como param no statement
	
	private Collection<FilterValue> values;//caso seja um filtro com vários valores
	
	private String strFilterBeforeOperator = null;// geralmente o nome da coluna EX: NOME_ALUNO = ?, se ficar null irá usar o aliasTab+"."+col.getName() (só terá o aliasTab. se a tab tiver alias) 
	private String operatorForValue = null; // = é o default. EX: =, <, >, ...
	private String strFilterAfterOperator = null; // se não for setado será ? para setar um valor depois

	
	// AND é o default. Se for o primeiro campo este operador não será concatenado. 
	// concatena sempre antes da strFilter
	// Se não quiser concatenar antes do campo setar como vazio.
	//private String operatorBetweenFilters = null; // EX: AND, OR ...
	
	// se for setado irá somente concatenar esta string e ignorar o strFilterBeforeOperator, operatorForValue e strFilterAfterOperator
	// se não for setado gera usando o strFilterBeforeOperator, operatorForValue e strFilterAfterOperator
	// não inclui o operatorBetweenFilters
	private String strForWhere = null; 
	
	// se quiser forçar para setar ou não setar o param
	// se for null verifica se entra como no where (ifEnterInWhere()) e se tem ? na strFilter
	private Boolean ifSetParamInQuery = null;

	// se entra no where (se concatena o getStrForWhere())
	// se ifEnterInWhere tiver null o padrão e só entrar se o value for diferente de null ou se tiver algum strFilterBeforeOperator ou operatorForValue ou strFilterAfterOperator setado
	//private Boolean ifEnterInWhere = null;
	
	
	public String getStrForWhere() {
		if(strForWhere==null){
			StringBuilder s = new StringBuilder();
			s.append(getStrFilterBeforeOperator());
			s.append(' ');// precisa do espaço! ex: se op for 'is null' ou se op for 'is' e after for 'null'
			s.append(getOperatorForValue());
			s.append(' ');// precisa do espaço! ex: se op for 'is null' ou se op for 'is' e after for 'null'
			s.append(getStrFilterAfterOperator());
			
			strForWhere = s.toString();// seta para manter a str gerada para melhorar o desempenho se chamar este método várias vezes, ver se terá casos que não deve ser setado
		}
		return strForWhere;
	}
	
	
	public boolean ifSetParamInQuery(){
		if(ifSetParamInQuery==null){
			return (ifEnterInWhere() && getStrFilterAfterOperator().indexOf('?')>=0);
		}else{
			return ifSetParamInQuery;
		}
	}
	
	public boolean ifEnterInWhere() {
		return true; // se adicionou o filtro deve entrar no where !!!!
		/*
		if(ifEnterInWhere==null){
			// se setar uma das strings significa que quer que entre no where
			return getValue()!=null || strFilterBeforeOperator!=null || operatorForValue!=null || strFilterAfterOperator!=null;
		}else{
			return ifEnterInWhere;
		}*/
	}
	
	public String getOperatorForValue() {
		if(operatorForValue==null) return DEFAULT_OPERATOR_FOR_VALUE;
		return operatorForValue;
	}
	//public String getOperatorBetweenFilters() {
	//	if(operatorBetweenFilters==null) return DEFAULT_OPERATOR_BETWEEN_FILTERS;
	//	return operatorBetweenFilters;
	//}
	public String getStrFilterAfterOperator() {
		//rever ... se o valor for null e quiser setar chamar set setStrFilterAfterOperator(DEFAULT_STR_FILTER_AFTER_OPERATOR)
		// ou ter método setStrFilterAfterOperatorForNullValue(){this.strFilterAfterOperator=DEFAULT_STR_FILTER_AFTER_OPERATOR;}
		// não tem sentido setar ter '?' se valor for null pois para filtros deve ser usado o IS NULL ou IS NOT NULL
		if(strFilterAfterOperator==null) {
			return DEFAULT_STR_FILTER_AFTER_OPERATOR;
		}else{
			return strFilterAfterOperator;
		}
	}
	
	
	//usado para quando um filtro tiver vários valores como IN (...) between ...
	public void setValuesSameType(Collection<T> values) throws SQLWrapperException {
		this.values = new ArrayList<>();
		for(T v : values){
			this.values.add(new FilterValue(getCol()!=null?getCol().getType():null, v));
		}
	}
	public void setValues(Collection<FilterValue> values) {
		this.values = values;
	}
	public Collection<FilterValue> getValues() {
		return values;
	}
	
	
	public T getValue() {
		return value;
	}

	
	public void setIfSetParamInQuery(Boolean ifSetParamInQuery) {
		this.ifSetParamInQuery = ifSetParamInQuery;
	}
	/*
	public void setIfEnterInWhere(Boolean ifEnterInWhere) {
		this.ifEnterInWhere = ifEnterInWhere;
	}*/
	public void setValue(T value) {
		this.value = value;
	}
	public String getStrFilterBeforeOperator() {
		if(strFilterBeforeOperator==null){
			StringBuilder s = new StringBuilder();
			if(getWrapperTab()!=null && getWrapperTab().getAliasTab()!=null) {
				s.append(getWrapperTab().getAliasTab());
				s.append('.');
			}
			s.append(getCol().getName());
			return s.toString();
		}
		return strFilterBeforeOperator;
	}
	public void setStrFilterBeforeOperator(String strFilterBeforeOperator) {
		this.strFilterBeforeOperator = strFilterBeforeOperator;
	}
	public void setOperatorForValue(String operatorForValue) {
		this.operatorForValue = operatorForValue;
	}
	public void setStrFilterAfterOperator(String strFilterAfterOperator) {
		this.strFilterAfterOperator = strFilterAfterOperator;
	}
	//public void setOperatorBetweenFilters(String operatorBetweenFilters) {
	//	this.operatorBetweenFilters = operatorBetweenFilters;
	//}
	public void setStrForWhere(String strForWhere) {
		this.strForWhere = strForWhere;
	}
	
}
